﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;

namespace Pixster.Controllers.Helpers
{
    public static class ImageResize
    {
        private const int MAX_WIDTH = 1200;
        private const int MAX_HEIGHT = 900;
        private const int THUMB_WIDTH = 200;
        private const int THUMB_HEIGHT = 150;

        public static void ResizeImage(string directory, string photoName)
        {
            try
            {
                var cacheDir = Path.Combine(directory, AppHelper.CacheDirctory);
                var thumbPath = Path.Combine(cacheDir, AppHelper.Thumbnail(photoName));

                if (File.Exists(thumbPath)) return;

                var image = Image.FromFile(Path.Combine(directory, photoName));

                var sizedImages = ResizeImage(image, thumbPath);

                if (sizedImages == null) return;

                sizedImages[0].Save(Path.Combine(cacheDir, AppHelper.DisplayImage(photoName)));
                sizedImages[1].Save(thumbPath);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
            }
        }

        public static List<Image> ResizeImage(Image image, string thumbPath)
        {
            var images = new List<Image>();

            var dimension = image.Width > image.Height ? Dimensions.Width : Dimensions.Height;

            if (image.Width > MAX_WIDTH || image.Height > MAX_HEIGHT)
            {
                // Constrain original image to maximum width and height
                images.Add(ConstrainProportions(image, dimension == Dimensions.Width ? MAX_WIDTH : MAX_HEIGHT, dimension));
            }
            else
            {
                // Keep original image intact, display by lightbox
                images.Add(image);
            }

            // Resize & pad the original image to the thumbnail size
            images.Add(FixedSize(image, THUMB_WIDTH, THUMB_HEIGHT));
            //images.Add(ConstrainProportions(image, dimension == Dimensions.Width ? THUMB_WIDTH : THUMB_HEIGHT, dimension));

            return images;
        }

        /// <summary>
        /// Scales an image by percentage
        /// </summary>
        /// <param name="imgPhoto"></param>
        /// <param name="percent"></param>
        /// <returns></returns>
        public static Image ScaleByPercent(Image imgPhoto, int percent)
        {
            var nPercent = ((float)percent / 100);

            var sourceWidth = imgPhoto.Width;
            var sourceHeight = imgPhoto.Height;
            const int sourceX = 0;
            const int sourceY = 0;

            const int destX = 0;
            const int destY = 0;
            var destWidth = (int)(sourceWidth * nPercent);
            var destHeight = (int)(sourceHeight * nPercent);

            var bmPhoto = new Bitmap(destWidth, destHeight, PixelFormat.Format24bppRgb);
            bmPhoto.SetResolution(imgPhoto.HorizontalResolution, imgPhoto.VerticalResolution);

            var grPhoto = Graphics.FromImage(bmPhoto);
            grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;

            grPhoto.DrawImage(imgPhoto,
                              new Rectangle(destX, destY, destWidth, destHeight),
                              new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight),
                              GraphicsUnit.Pixel);

            grPhoto.Dispose();
            return bmPhoto;
        }

        /// <summary>
        /// This resizes the image to a maximum size, width or height depending on dimension
        /// </summary>
        /// <param name="imgPhoto"></param>
        /// <param name="size"></param>
        /// <param name="dimension"></param>
        /// <returns></returns>
        private static Image ConstrainProportions(Image imgPhoto, int size, Dimensions dimension)
        {
            var sourceWidth = imgPhoto.Width;
            var sourceHeight = imgPhoto.Height;
            const int sourceX = 0;
            const int sourceY = 0;
            const int destX = 0;
            const int destY = 0;
            float nPercent;

            switch (dimension)
            {
                case Dimensions.Width:
                    nPercent = (size / (float)sourceWidth);
                    break;
                default:
                    nPercent = (size / (float)sourceHeight);
                    break;
            }

            var destWidth = (int)(sourceWidth * nPercent);
            var destHeight = (int)(sourceHeight * nPercent);

            var bmPhoto = new Bitmap(destWidth, destHeight, PixelFormat.Format24bppRgb);
            bmPhoto.SetResolution(imgPhoto.HorizontalResolution, imgPhoto.VerticalResolution);

            var grPhoto = Graphics.FromImage(bmPhoto);
            grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;

            grPhoto.DrawImage(imgPhoto,
                              new Rectangle(destX, destY, destWidth, destHeight),
                              new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight),
                              GraphicsUnit.Pixel);

            grPhoto.Dispose();
            return bmPhoto;
        }

        /// <summary>
        /// Resizes and pads an image to a specific width & heigh
        /// </summary>
        /// <param name="imgPhoto"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <returns></returns>
        private static Image FixedSize(Image imgPhoto, int width, int height)
        {
            var sourceWidth = imgPhoto.Width;
            var sourceHeight = imgPhoto.Height;
            const int sourceX = 0;
            const int sourceY = 0;
            var destX = 0;
            var destY = 0;

            float nPercent;

            var nPercentW = (width / (float)sourceWidth);
            var nPercentH = (height / (float)sourceHeight);

            //if we have to pad the height pad both the top and the bottom
            //with the difference between the scaled height and the desired height
            if (nPercentH < nPercentW)
            {
                nPercent = nPercentH;
                destX = (int)((width - (sourceWidth * nPercent)) / 2);
            }
            else
            {
                nPercent = nPercentW;
                destY = (int)((height - (sourceHeight * nPercent)) / 2);
            }

            var destWidth = (int)(sourceWidth * nPercent);
            var destHeight = (int)(sourceHeight * nPercent);

            var bmPhoto = new Bitmap(width, height, PixelFormat.Format24bppRgb);
            bmPhoto.SetResolution(imgPhoto.HorizontalResolution, imgPhoto.VerticalResolution);

            var grPhoto = Graphics.FromImage(bmPhoto);
            grPhoto.Clear(Color.Transparent);
            grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;

            grPhoto.DrawImage(imgPhoto,
                              new Rectangle(destX, destY, destWidth, destHeight),
                              new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight),
                              GraphicsUnit.Pixel);

            grPhoto.Dispose();
            return bmPhoto;
        }

        /// <summary>
        /// Crops an image to a specific width & height
        /// </summary>
        /// <param name="imgPhoto"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="anchor"></param>
        /// <returns></returns>
        public static Image Crop(Image imgPhoto, int width, int height, AnchorPosition anchor)
        {
            var sourceWidth = imgPhoto.Width;
            var sourceHeight = imgPhoto.Height;
            const int sourceX = 0;
            const int sourceY = 0;
            var destX = 0;
            var destY = 0;

            float nPercent;

            var nPercentW = (width / (float)sourceWidth);
            var nPercentH = (height / (float)sourceHeight);

            if (nPercentH < nPercentW)
            {
                nPercent = nPercentW;
                switch (anchor)
                {
                    case AnchorPosition.Top:
                        destY = 0;
                        break;
                    case AnchorPosition.Bottom:
                        destY = (int)(height - (sourceHeight * nPercent));
                        break;
                    default:
                        destY = (int)((height - (sourceHeight * nPercent)) / 2);
                        break;
                }
            }
            else
            {
                nPercent = nPercentH;
                switch (anchor)
                {
                    case AnchorPosition.Left:
                        destX = 0;
                        break;
                    case AnchorPosition.Right:
                        destX = (int)(width - (sourceWidth * nPercent));
                        break;
                    default:
                        destX = (int)((width - (sourceWidth * nPercent)) / 2);
                        break;
                }
            }

            var destWidth = (int)(sourceWidth * nPercent);
            var destHeight = (int)(sourceHeight * nPercent);

            var bmPhoto = new Bitmap(width, height, PixelFormat.Format24bppRgb);
            bmPhoto.SetResolution(imgPhoto.HorizontalResolution, imgPhoto.VerticalResolution);

            var grPhoto = Graphics.FromImage(bmPhoto);
            grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;

            grPhoto.DrawImage(imgPhoto,
                              new Rectangle(destX, destY, destWidth, destHeight),
                              new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight),
                              GraphicsUnit.Pixel);

            grPhoto.Dispose();
            return bmPhoto;
        }

        #region Nested type: AnchorPosition

        public enum AnchorPosition
        {
            Top,
            Center,
            Bottom,
            Left,
            Right
        }

        #endregion

        #region Nested type: Dimensions

        private enum Dimensions
        {
            Width,
            Height
        }

        #endregion
    }
}